﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Configuration;
using BMS.DataAccess;
using BMS.ConvertToEntity;
using BMS.Entity;
using log4net;

namespace BMS.BL
{

    public class BiometricProcess
    {

        #region Attributes

        private DAO _dataAccess;
        private Transaction _entities;
        private Functionary _functionary;
        private TurnAccess _turnAccess;
        private readonly ILog _log = LogManager.GetLogger(typeof(BiometricProcess));

        #endregion

        #region Methods

        /// <summary>
        /// Constructor de la clase
        /// </summary>
        public BiometricProcess()
        {
            _dataAccess = new DAO();

            _entities = new Transaction();

            _functionary = new Functionary();

            _turnAccess = new TurnAccess();

        }


        /// <summary>
        /// Verifica si el ingreso es para un funcionario o para una persona
        /// </summary>
        /// <param name="enrollNumber">Identificador de la persona ante el lector biométrico</param>
        /// <param name="messages">Eventos generados por las validaciones</param>
        /// <returns></returns>
        public bool CanEntry(string enrollNumber, out List<string> messages)
        {
            messages = new List<string>();

            try
            {
                _log.Debug(string.Format("Validando si el usuario: [{0}] es un funcionario", enrollNumber));

                DataTable tableFunctionaries = _dataAccess.GetFunctionary(enrollNumber);

                if (tableFunctionaries != null && tableFunctionaries.Rows.Count > 0)
                {
                    _functionary = _entities.GetFunctionaryFromDataTable(tableFunctionaries);

                    _log.Debug(string.Concat("Validando si el funcionario : [", _functionary.Name, "] con identificador en lector : [", _functionary.IdInBiometricDevice, "] tiene programación"));

                    return (CanEntryFunctionary(enrollNumber, out messages));
                }

            }
            catch (Exception exc)
            {
                _log.Error(string.Format("Se presentaron inconvenientes realizando la validación para el acceso del usuario: {0}, Mensaje del sistema: {1}", enrollNumber, exc.ToString()));
            }

            return false;
        }

        /// <summary>
        /// Valida si un funcionario puede ingresar a las instalaciones de una empresa
        /// </summary>
        /// <param name="enrollNumber">Identificador del usuario</param>
        /// <param name="messages">Validaciones realizadas</param>
        /// <returns>True si el funcionario tiene acceso y False en caso de que esta no tenga acceso</returns>
        public bool CanEntryFunctionary(string enrollNumber, out List<string> messages)
        {
            messages = new List<string>();

            try
            {
                DataTable tableTurnFunctionary = _dataAccess.GetDataFromTurnFunctionary(enrollNumber);

                _turnAccess = _entities.GetTurnAccessFromDataTable(tableTurnFunctionary);

                _turnAccess.Functionary = _functionary;

                _log.Debug(string.Format("Validando si el funcionario : [{0}] esta programado", _functionary.Name));

                bool createAuthorization = false;

                // Si es un usuario con privilegios especiales
                if (_turnAccess.CanAlwaysEntry)
                {
                    _log.Debug(string.Format("El funcionario {0} tiene privilegios de acceso sin validación de turno.", _functionary.Name));
                    return true;
                }
                // Se valida que el usuario este programado
                if (tableTurnFunctionary != null & tableTurnFunctionary.Rows.Count > 0)
                {
                    _turnAccess = _entities.GetTurnAccessFromDataTable(tableTurnFunctionary);

                    _log.Debug(string.Format("La informacion del turno para el funcionario: [{0}] es la siguiente : Turno : [{1}], Fecha del turno: [{2}], Minutos de tolerancia antes de ingreso : [{3}],  Minutos de tolerancia despues de ingreso : [{4}]", _functionary.Name, _turnAccess.Turn, _turnAccess.TurnDate, _turnAccess.MinutesBeforeEntry, _turnAccess.MinutesAfterEntry));

                    _log.Debug(string.Format("Validando si el funcionario : [{0}] llego antes de lo programado", _functionary.Name));

                    if (IsFunctionaryEarlyToEntry(_turnAccess))
                    {
                        _log.Debug(string.Format("El funcionario : [{0}] llego antes de lo programado. Hora de llegada : [{1}]", _functionary.Name, _turnAccess.ActualDate));

                        return CanEntryFunctionaryWithAutorization(_functionary, createAuthorization, ConfigurationManager.AppSettings["Reason_Message_EntryBeforeHour"].ToString());
                    }
                    _log.Debug(string.Format("Validando si el funcionario : [{0}] llego despues de lo programado. Hora de llegada : [{1}]", _functionary.Name, _turnAccess.ActualDate));

                    if (IsFunctionaryLateToEntry(_turnAccess))
                    {
                        _log.Debug(string.Format("El funcionario : [{0}]  llego despues de lo programado. Hora de llegada : [{1}]", _functionary.Name, _turnAccess.ActualDate));

                        return CanEntryFunctionaryWithAutorization(_functionary, createAuthorization, ConfigurationManager.AppSettings["Reason_Message_EntryAfterHour"].ToString());
                    }

                    _log.Debug(string.Format("El funcionario : [{0}]  llego en el horario programado.", _functionary.Name));

                    return true;

                }
                else
                {
                    _log.Debug("El funcionario no esta programado, verificando si tiene autorizacion de ingreso");

                    string message = string.Empty;

                    return CanEntryFunctionaryWithAutorization(_functionary, createAuthorization, ConfigurationManager.AppSettings["Reason_Message_EntryWithoutProgramming"].ToString());
                }

            }
            catch (Exception exc)
            {
                messages.Add(string.Concat("...Error: ", exc.ToString()));

                _log.Error(string.Format("Se presentaron inconvenientes en la validación de la programación de turnos para el funcionario: {0}, Mensaje retornado por el sistema: {1}", enrollNumber, exc.ToString()));
            }


            return false;
        }

        private bool CanEntryFunctionaryWithAutorization(Functionary _functionary, bool createAuthorization, string message)
        {
            if (!HasAuthorizationToEntry(_functionary, out createAuthorization))
            {
                if (createAuthorization)
                    AddRequestForEntryAuthorization(_functionary, message);

                return false;
            }
            return true;
        }

        private bool IsFunctionaryEarlyToEntry(TurnAccess turnAccess)
        {
            DateTime turnDate = turnAccess.TurnDate.AddMinutes(-turnAccess.MinutesBeforeEntry);
            if (DateTime.Now < turnDate)
                return true;

            return false;
        }

        private bool IsFunctionaryLateToEntry(TurnAccess turnAccess)
        {
            DateTime turnDate = turnAccess.TurnDate.AddMinutes(turnAccess.MinutesAfterEntry);
            if (DateTime.Now > turnDate)
                return true;

            return false;
        }

        private bool HasAuthorizationToEntry(Functionary functionary, out bool createAuthorization)
        {
            _log.Debug("Validando si el funcionario tiene aprobaciones pendientes");

            DataTable authorization = _dataAccess.CanEntryByAuthorization(functionary.IdInBiometricDevice);

            createAuthorization = false;

            if (authorization != null && authorization.Rows.Count > 0)
            {
                _log.Debug("Se detectó información para las autorizaciones");

                bool isAuthorized = false;

                bool.TryParse(authorization.Rows[0][0].ToString().Trim(), out isAuthorized);

                _log.Debug(string.Format("¿Está autorizado el funcionario? {0}", isAuthorized ? "Si" : "No"));

                if (isAuthorized)
                {
                    _log.Debug("El funcionario tiene un registro aprobado");
                    return true; /// tiene registro en autorizacion aprobado
                }

                _log.Debug("El funcionario no tiene un registro aprobado");

                return false;
            }

            _log.Debug("El funcionario no aprobaciones");

            createAuthorization = true;

            return false;

        }

        private void AddRequestForEntryAuthorization(Functionary functionary, string message)
        {
            _dataAccess.CreateAuthorization(functionary.IdInBiometricDevice, ConfigurationManager.AppSettings["ID_PersonAuthorizing"], functionary.Name, functionary.Charge, functionary.Headquarter, string.Empty, functionary.Company, ConfigurationManager.AppSettings["IP"], message);
        }

        /// <summary>
        /// Valida si una persona puede ingresar a las instalaciones de una empresa
        /// </summary>
        /// <param name="enrollNumber">Identificador del usuario</param>
        /// <param name="messages">Validaciones realizadas</param>
        /// <returns>True si la persona tiene acceso y False en caso de que esta no tenga acceso</returns>
        public bool CanEntryPerson(string enrollNumber, out List<string> messages)
        {
            messages = new List<string>();

            try
            {
                if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["Execute"]))
                {
                    DataTable tablePersons = _dataAccess.GetDataFromPerson(enrollNumber);

                    DataRow[] row = tablePersons.Select();

                    if (row.Length > 0)
                    {
                        messages.Add(string.Concat("...Puede ingresar?: ", row[0]["CAN_ENTRY"].ToString()));
                        messages.Add(string.Concat("...Número de documento de identificación: ", row[0]["DOCUMENT_NUMBER"].ToString()));
                        messages.Add(string.Concat("...Nombre de la persona: ", row[0]["NAME"].ToString()));

                        return true;
                    }
                    else
                    {
                        messages.Add(string.Concat("...Descripción: ", "La persona no existe"));
                    }
                }
            }
            catch (Exception exc)
            {
                messages.Add(string.Concat("...Error: ", exc.ToString()));

                _log.Error(string.Format("Se presentaron inconvenientes en la validación de la agenda para el usuario: {0}, Mensaje retornado por el sistema: {1}", enrollNumber, exc.ToString()));
            }

            return false;
        }

        /// <summary>
        /// Realiza la creación del registro para el ingreso de la persona
        /// </summary>
        /// <param name="sEnrollNumber">Identificación de la persona ante el lector</param>
        /// <returns>True si es permitida la salida de la persona</returns>
        public bool CreateEntryRegister(string sEnrollNumber)
        {
            Functionary functionary = _entities.GetFunctionaryFromDataTable(_dataAccess.GetFunctionary(sEnrollNumber));

            return _dataAccess.CreateAuditRegister(sEnrollNumber, functionary.DocumentNumber, functionary.Name, functionary.Charge, functionary.Headquarter, functionary.CostCenter, functionary.Company, ConfigurationManager.AppSettings["IP"]);
        }

        /// <summary>
        /// Realiza la creación del registro para salida de la persona
        /// </summary>
        /// <param name="sEnrollNumber">Identificación de la persona ante el lector</param>
        /// <returns>True si es permitida la salida de la persona</returns>
        public bool CreateOutRegister(string sEnrollNumber)
        {
            Functionary functionary = _entities.GetFunctionaryFromDataTable(_dataAccess.GetFunctionary(sEnrollNumber));

            return _dataAccess.CreateAuditRegister(sEnrollNumber, functionary.DocumentNumber, functionary.Name, functionary.Charge, functionary.Headquarter, functionary.CostCenter, functionary.Company, ConfigurationManager.AppSettings["IP"]);
        }

        public void CreateDeviceRegister(string ipDevice, string state, string observations)
        {
            _dataAccess.CreateDeviceRegister(ipDevice, state, observations);
        }

        #endregion

    }
}
